from openai import OpenAI
import gradio as gr
from tenacity import retry, wait_random_exponential, stop_after_attempt
import requests
import json

api_key = "sk-xxx"  # Replace with your key
client = OpenAI(api_key=api_key)
GPT_MODEL = "gpt-3.5-turbo"


@retry(wait=wait_random_exponential(multiplier=1, max=40), stop=stop_after_attempt(3))
def chat_completion_request(messages, tools=None, tool_choice=None, model=GPT_MODEL, temperature=1.0):
    try:
        response = client.chat.completions.create(
            messages=messages,
            model=model,
            temperature=temperature,
            tool_choice=tool_choice,
            tools=tools
        )
        return response
    except Exception as e:
        raise gr.Error(f"Unable to generate ChatCompletion response.\nException: {e}")


tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "获取实时的天气预报",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市，比如：深圳",
                    },
                },
                "required": ["location"],
            },
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_n_day_weather",
            "description": "获取未来N天的天气预报",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市，比如：深圳",
                    },
                    "num_days": {
                        "type": "integer",
                        "description": "需要预测的天数，只能是7或者15",
                    },
                },
                "required": ["location", "num_days"],
            },
        }
    },
]


# appcode 从这获取 第三方天气预报API：http://localhost:8888/lab?token=422b4c37fbacb14d5f720f946bd313cb160b9aaf237f259a
def get_current_weather_response(location, appcode="xxx"):  # Replace with your APPCODE
    url = 'https://getweather.market.alicloudapi.com/lundear/weather1d'
    querystring = {
        'areaCode': '',
        'areaCn': location,
        'ip': '',
        'lng': '',
        'lat': '',
        'needalarm': '0',
        'need1hour': '0',
        'need3hour': '0',
        'needIndex': '0',
        'needObserve': '0'
    }
    headers = {
        'Authorization': 'APPCODE ' + appcode
    }
    response = requests.request("GET", url, headers=headers, params=querystring)
    if response.status_code == 200:
        return response.json()
    else:
        return {"error": f"获取 {location} 的天气信息失败"}


def get_n_day_weather_response(location, num_days, appcode="xxx"):  # Replace with your APPCODE
    if num_days not in [7, 15]:
        return {"error": "num_days should be 7 or 15."}
    url = f"https://getweather.market.alicloudapi.com/lundear/weather{num_days}d"
    querystring = {
        'areaCode': '',
        'areaCn': location,
        'ip': '',
        'lng': '',
        'lat': '',
        'needalarm': '0',
        'need1hour': '0',
        'need3hour': '0',
        'needIndex': '0',
        'needObserve': '0'
    }
    headers = {
        'Authorization': 'APPCODE ' + appcode
    }
    response = requests.request("GET", url, headers=headers, params=querystring)
    if response.status_code == 200:
        return response.json()
    else:
        return {"error": f"获取 {location} 的天气信息失败"}


delimiter = '###'


def get_current_weather(location):
    current_weather = get_current_weather_response(location)
    if "error" in current_weather:
        return current_weather["error"]
    else:
        weather_data = current_weather["data"]
        message = {
            "role": "system",
            "content": f"你是一个天气预报助手，可以根据由{delimiter}分隔的某地区的实时天气信息，获取该地区的实时天气情况，并且还可以给用户提出针对当前天气情况的出行或穿衣建议。{delimiter}{weather_data}{delimiter}"
        }
        response = chat_completion_request([message])
        return response.choices[0].message.content


def get_n_day_weather(location, num_days):
    n_day_weather = get_n_day_weather_response(location, num_days)
    if "error" in n_day_weather:
        return n_day_weather["error"]
    else:
        weather_data = n_day_weather["data"]
        message = {
            "role": "system",
            "content": f"你是一个天气预报助手，可以根据由{delimiter}分隔的某地区未来{num_days}天的天气信息，获取该地区未来{num_days}天的天气情况，并且还可以给用户提出针对未来天气情况的出行或穿衣建议。{delimiter}{weather_data}{delimiter}"
        }
        response = chat_completion_request([message])
        return response.choices[0].message.content


def execute_function_call(message):
    calls = message.tool_calls
    results = []
    for call in calls:
        function = call.function
        if function.name == "get_current_weather":
            location = json.loads(function.arguments)["location"]  # json.loads() 用于将 json 格式的字符串转换成Python字典
            results.append(get_current_weather(location))
        elif function.name == "get_n_day_weather":
            location = json.loads(function.arguments)["location"]
            num_days = json.loads(function.arguments)["num_days"]
            results.append(get_n_day_weather(location, num_days))
        else:
            raise gr.Error(f"Error: function {function.name} does not exist.")
    return results


def predict(message, history):
    history_openai_format = [
        {
            "role": "system",
            "content": "Don't make assumptions about what values to plug into functions. Ask for clarification if a "
                       "user request is ambiguous."
        }
    ]
    for human, assistant in history:
        history_openai_format.append({"role": "user", "content": human})
        history_openai_format.append({"role": "assistant", "content": assistant})
    history_openai_format.append({"role": "user", "content": message})

    chat_response = chat_completion_request(history_openai_format, tools=tools)
    assistant_message = chat_response.choices[0].message
    results = execute_function_call(assistant_message)

    res = ""
    for result in results:
        res += result + "\n"
    return res


app = gr.ChatInterface(
    predict,
    title="WeatherCast Pro",
    description="WeatherCast Pro 是一个功能强大的天气预报助手，提供实时天气信息和未来7/15天的天气预报。"
                "用户可以轻松查看多个城市的实时天气状况，包括温度、湿度、风速等详细数据，并获取准确的气象预报信息。"
                "无论是出行计划、生活日常还是户外活动，WeatherCast Pro都能帮助您做出明智的天气决策，让您的生活更加便捷和安心。"
                "立即使用 WeatherCast Pro，随时掌握天气动向！"
)
app.launch(share=True)  # share=True for Running on public URL
